package org.codingeasy.streamrecord.springboot.configuration;


import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.codingeasy.streamrecord.core.ComponentFactory;
import org.codingeasy.streamrecord.core.DefaultParamParse;
import org.codingeasy.streamrecord.core.ParamParse;
import org.codingeasy.streamrecord.core.Pipeline;
import org.codingeasy.streamrecord.core.RecordContext;
import org.codingeasy.streamrecord.core.RecordProducer;
import org.codingeasy.streamrecord.core.processor.ProcessorStrategy;
import org.codingeasy.streamrecord.core.processor.RecordPostProcessor;
import org.codingeasy.streamrecord.core.processor.SpElTemplateResolve;
import org.codingeasy.streamrecord.core.processor.TemplateResolve;
import org.codingeasy.streamrecord.core.support.RecordPointcutAdvisor;
import org.codingeasy.streamrecord.core.utils.RuntimeUtils;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.util.CollectionUtils;

/**
 * 自动装配类
 *
 * @author : KangNing Hu
 */
@ConditionalOnProperty(prefix = StreamRecordProperties.PREFIX, name = "enabled", matchIfMissing = true, havingValue = "true")
@EnableConfigurationProperties(StreamRecordProperties.class)
public class StreamRecordAutoConfiguration {


  public static final String EXECUTOR_BEAN_NAME = "StreamRecordAutoConfiguration#executor";


  @Bean(EXECUTOR_BEAN_NAME)
  @ConditionalOnMissingBean(name = EXECUTOR_BEAN_NAME)
  @ConditionalOnProperty(prefix = StreamRecordProperties.PREFIX, name = "thread-poll-name", havingValue = EXECUTOR_BEAN_NAME, matchIfMissing = true)
  public Executor streamRecordExecutor() {
    int count = RuntimeUtils.availableProcessors(4);
    return new ThreadPoolExecutor(count / 2,
        count + 1,
        30,
        TimeUnit.MINUTES,
        new SynchronousQueue<Runnable>(),
        new BasicThreadFactory
            .Builder()
            .namingPattern("Record-Thread-")
            .build()
    );
  }

  @ConditionalOnMissingBean
  @Bean
  public ParamParse paramParse() {
    return new DefaultParamParse();
  }


  @ConditionalOnMissingBean
  @Bean
  public TemplateResolve spElTemplateResolve() {
    return new SpElTemplateResolve();
  }


  @ConditionalOnMissingBean
  @Bean
  public ComponentFactory springComponentFactory() {
    return new SpringComponentFactory();
  }


  @Bean
  public RecordContext recordContext(
      @Autowired(required = false) List<ProcessorStrategy> processorStrategies,
      @Autowired(required = false) List<RecordPostProcessor> recordPostProcessors,
      @Autowired(required = false) @Qualifier("defaultRecordProducer") RecordProducer defaultRecordProducer,
      @Autowired(required = false) @Qualifier("defaultPipeline") Pipeline defaultPipeline,
      ApplicationContext applicationContext, StreamRecordProperties properties,
      TemplateResolve templateResolve, ParamParse paramParse, ComponentFactory componentFactory) {
    RecordContext recordContext = new RecordContext();
    //执行策略 没有则不设置
    if (!CollectionUtils.isEmpty(processorStrategies)) {
      processorStrategies.forEach(recordContext::addProcessorStrategy);
    }
    //后置处理器设置 没有则不设置
    if (!CollectionUtils.isEmpty(recordPostProcessors)) {
      recordPostProcessors.forEach(recordContext::addRecordPostProcessor);
    }
    //模板解析器设置 没有则不设置
    recordContext.setTemplateResolve(templateResolve);
    //参数解析器设置 没有则不设置
    recordContext.setParamParse(paramParse);
    //全局日志生成器设置 没有则不设置
    if (defaultRecordProducer != null) {
      recordContext.setDefaultRecordProducer(defaultRecordProducer);
    }
    //全局生成默认管道设置 没有则不设置
    if (defaultPipeline != null) {
      recordContext.setDefaultPipeline(defaultPipeline);
    }
    //线程池
    String threadPollName = properties.getThreadPollName();
    if (!StringUtils.isEmpty(threadPollName)) {
      recordContext.setExecutor(applicationContext.getBean(threadPollName, Executor.class));
    }
    //设置组件工厂
    recordContext.setComponentFactory(componentFactory);
    //初始化组件
    recordContext.initComponent();
    //初始化
    return recordContext;
  }

  @Bean
  public PointcutAdvisor recordPointcutAdvisors(RecordContext recordContext) {
    return new RecordPointcutAdvisor(recordContext);
  }

  @Bean
  public RecordDefinitionAutoRegistry recordPointcutAdvisor(RecordContext recordContext) {
    return new RecordDefinitionAutoRegistry(recordContext);
  }
}
